<?php

namespace LDL\Twspider;

/*use LDL\Twspider\C\Timeout;
use LDL\Twspider\C\TimeRun;*/
use LDL\Twspider\C\StackArray;
use LDL\Twspider\C\Timer\ThreadJob;
use LDL\Twspider\E\TwDefFactory;
use Lin\Nsg\NatursUri;
use VDB\Spider\Stackable;

/**
 * @property workIdMax
 * @property metaData
 * Class Spider
 * @package LDL\Twspider
 */

class Spider
    //extends \Volatile
{
    /**
     *
     * @var Twspider
     */
    private $_sp ;
    /**
     * @var TwPool
     */
    protected $_pool ;

    protected function twspider($url = null, $configFactory = null, $metaData = null)
    {
        return new Twspider($url, $configFactory);
    }

    public function __construct($url = null, $configFactory = null, $metaData = null)
    {
        global $LOGGER, $_Q;
        //
        //try {
        if ($configFactory == null) {
            $configFactory = new TwDefFactory(__DIR__ . "/../../../../../..");
        }

        $this->_sp = $this->twspider($url, $configFactory);//这里会进__set

        //debug($this->twspider($url, $configFactory));
//		、、var_dump($LOGGER);exit ;
        $this->logger = $LOGGER;

        $this->prepareCrawl();
        //var_dump($_Q);
        $_Q = $this->getQueueManager();
        //var_dump($_Q);
        // debug("xxx");
        //$this->_sp->getQueueManager();// bug ? in php7,must setFirst ,if set IN member ,the new Object will no refenence,will destroyed

        if (!empty($url)) {
            $_Q->addUri($this->getUri($url));
        }
        //	$_Q->addUri($this->_sp->getUri($url));
        //seed ?????????????? ?í??  ?ò?? SUBMIT???ê?????° WORKer??????

        //}
        //	debug($this->_sp->getQueueManager()->count()) ;
        //$this->metaData = Stackable::instance(array_merge(is_array($metaData) ? $metaData : array(), array("spider" => &$this)));
        $this->metaData = Stackable::instance(is_array($metaData) ? $metaData : array());
      //  $this->metaData['spider']=$this;
        $poolName = $this->getPoolClassName();
       // $worknum=;
        //$this->workIdMax= $worknum -3 ;
        $this->_pool=new $poolName($this->workNum(), $this->getWorkClassName(), [$this->_sp, $this]);
        $this->_sp->pool=$this->_pool;
    }
    /*public function worker_list(){
        if ($this->workers !== null)
            return array_keys($this->workers);
        return null;
    }*/
    public function getWorkClassName()
    {
        return TwWorker::class;
    }
    public function getPoolClassName()
    {
        return TwPool::class;
    }

    public function __call($method, $arguments)
    {
        return call_user_func_array(array($this->_sp, $method), $arguments);
        //echo  $method."is not found \n";
    }

    public static function __callStatic($method, $arguments)
    {
        return call_user_func_array(array(Twspider::class, $method), $arguments);

        ///	echo "$method\n" ;
    }

    private function isfromBuild($key){
        $is= in_array($key,array('_sp','_pool',"metaData","started"),true);
        return $is;
    }
    function __get($name)
    {
       if($this->isfromBuild($name)){
           return $this->{$name};
       }else{
           return $this->_sp->{$name};
       }
    }
    function __set($name, $value)
    {
        if($this->isfromBuild($name)){
            $this->{$name}=$value;
        }else{
            $this->_sp->{$name}=$value;
        }
    }

    /**
     * 这个像JAVA  要在主MAIN 去启用 不然 程序 就结束 了
     */
    public function join()
    {
        if (!$this->started) {
            throw new \RuntimeException("nicht started");
        }

        /*$this->collect(function($work){
               var_dump("working is ".get_class($work));
               sleep(10);

               return false;
           });*/
    /*   while(true){

           var_dump("collect");

          // var_dump( $this->aaaa);
           $this->dump();

            //可以看出没有实现stackable的 对象被复制了 pool也被复制了 。所以task里调用和主线程调用打印不一致
           sleep(10);
        }*/

         $this->_pool->shutdown();
    }

    protected function taskNum()
    {
        return 2;
    }
    protected function workNum(){
        return $this->taskNum()+3;
    }

    public function start()
    {
        $this->started = true;
        $workIdMax = $this->workNum();
        for ($i =0; $i <=$workIdMax; $i++) {
            $iid=$this->_pool->submit(new \Threaded());//如果$this- extends thread ,调用 会block
            $this->logger->debug("初始化通道%d:" ,$iid);
        }
        $this->nachChan();

    }
    protected function nachChan(){
        $tasknum = $this->taskNum();
        for ($i = $tasknum; $i > 0; $i--) {
            $workid=$this->addTask($this->getSpiderTask());
            $this->logger->debug("启动任务%02d ,所在通道%d:" ,($tasknum + 1 - $i),$workid);
        }
    }
    public $started = false;

    protected function getSpiderTask()
    {
        return new Task();
    }

    /**
     * 加入1-max 通道 ，0-2号不加入 做为特殊 备用
     * @return int workId
     */
    public  function workidIfNull(){
        return rand(3,$this->workNum()-1);
    }
    /**
     * @param C\SpThreaded $task
     * @param null $workId 不指定就打入不为0的通道
     * @return int
     */
   final public function addTask(C\SpThreaded $task,$workId=null)
   {
       if (!$this->started) {
           throw new \RuntimeException("nicht started");
       }
       if ($workId === null) {
           $workId = $this->workidIfNull();
       }
       $reid = null;
       try {
         //  $this->dump();
           $reid = $this->_pool->submitTo($workId, $task);
       } catch (\Exception $e) {
           $this->logger->error($e->getMessage());
           // parent::submit(new \Threaded());//此方法必须在shutdown之前调用  属于初始化方法
       }
       return $reid;
   }

    /**
     * 添加定时任务 默认0号通道
     * @param $micro_seconds
     * @param $func
     * @param int $workid
     * @return int
     */
    public function addCrontask($micro_seconds,$func,$workid=0){
       return $this->addTask(ThreadJob::getInstance($micro_seconds,$func),$workid);
    }
   /*
    * task里添加task TODO
    */
    final public function forckTask(C\SpThreaded $task,$workId=null)
    {
        return $this->addTask($task,$workId);

        /* $this->_sp->getQueueManager()->addUri(NatursUri::getUri("http://www.badu1.com"));
         $this->_sp->getQueueManager()->addUri(NatursUri::getUri("http://www.bad1u1.com"));
         $this->logger->debug("wait 30");
         sleep(30);*/
    }

}
